package com.gy.hadoop.juc.volatile_;


/*
 volatile是JVM提供的轻量的同步机制
     1.保证可见性
        多线程间变量的修改对其它线程可见
     2.保证循序，禁止指令重排序
        volatile底层是用CPU的**内存屏障**（Memory Barrier）指令来实现的，有两个作用，
        一个是保证特定操作的顺序性，
        二是保证变量的可见性。在指令之间插入一条Memory Barrier指令，告诉编译器和CPU，在Memory Barrier指令之间的指令不能被重排序
     3.不保证原子性
        async
        juc.atomicInteger
 */

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 可见性测试
 */
public class MyData {

    public static void main(String[] args) {
//        volatileTest();
//        volatileAtomic();
        volatileVotaile();
        Lock lock = new ReentrantLock();
        lock.lock();
        lock.unlock();
    }


    int number = 0;

    AtomicInteger atomicInteger = new AtomicInteger();

    volatile int number_votaile = 0;


    /**
     * 普通方式
     */
    private static void volatileTest() {
        System.out.println(Thread.currentThread().getName() + "\t" + "volatileVisibilityDemo 测试");
        MyData myData = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "修改前的值: " + myData.number);
            try {
                TimeUnit.SECONDS.sleep(3);
                myData.number++;
                System.out.println(Thread.currentThread().getName() + "\t" + "修改后的值: " + myData.number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "daemons").start();

        while (myData.number == 0) {
            //main线程持有共享数据的拷贝，一直为0

        }
        //TODO 代码能走到这里说明内存的指修改后被读取到了
        System.out.println(Thread.currentThread().getName() + "\t" + "测试通过: " + myData.number);
    }


    /**
     * atomic方式
     */
    private static void volatileAtomic() {
        System.out.println(Thread.currentThread().getName() + "\t" + "volatileAtomic 测试");
        MyData myData = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "修改前的值: " + myData.atomicInteger.get());
            try {
                TimeUnit.SECONDS.sleep(3);
                myData.atomicInteger.getAndIncrement();
                System.out.println(Thread.currentThread().getName() + "\t" + "修改后的值: " + myData.atomicInteger.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "daemons").start();

        while (myData.atomicInteger.get() == 0) {
            //main线程持有共享数据的拷贝，一直为0

        }
        //TODO 代码能走到这里说明内存的指修改后被读取到了
        System.out.println(Thread.currentThread().getName() + "\t" + "测试通过: " + myData.atomicInteger.get());
    }


    /**
     * votaile方式
     */
    private static void volatileVotaile() {
        System.out.println(Thread.currentThread().getName() + "\t" + "volatileVotaile 测试");
        MyData myData = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "修改前的值: " + myData.number_votaile);
            try {
                TimeUnit.SECONDS.sleep(3);
                myData.number_votaile++;
                System.out.println(Thread.currentThread().getName() + "\t" + "修改后的值: " + myData.number_votaile);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "daemons").start();

        while (myData.number_votaile == 0) {
            //main线程持有共享数据的拷贝，一直为0

        }
        //TODO 代码能走到这里说明内存的指修改后被读取到了
        System.out.println(Thread.currentThread().getName() + "\t" + "测试通过: " + myData.number_votaile);
    }


    /*

这个漏洞比较tricky，很难捕捉，但是是存在的。`instance=new SingletonDemo();`可以大致分为三步
```java
memory = allocate();     //1.分配内存
instance(memory);	 //2.初始化对象
instance = memory;	 //3.设置引用地址
```

其中2、3没有数据依赖关系，**可能发生重排**。如果发生，此时内存已经分配，那么`instance=memory`不为null。如果此时线程挂起，`instance(memory)`还未执行，对象还未初始化。由于`instance!=null`，所以两次判断都跳过，最后返回的`instance`没有任何内容，还没初始化。

解决的方法就是对`singletondemo`对象添加上`volatile`关键字，禁止指令重排。

     */
    public static class SingletonDemo {
        private static SingletonDemo singletonDemo = null;

        private SingletonDemo() {
            System.out.println(Thread.currentThread().getName() + "\t 我是构造方法,指令重排测试");
        }

        //DCL模式 Double Check Lock 双端检索机制：在加锁前后都进行判断
        public static SingletonDemo getInstance() {
            if (singletonDemo == null) {
                synchronized (SingletonDemo.class) {
                    if (singletonDemo == null) {
                        singletonDemo = new SingletonDemo();
                    }
                }
            }
            return singletonDemo;
        }

        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    SingletonDemo.getInstance();
                }, String.valueOf(i + 1)).start();
            }
        }
    }

}
